package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.bind.WebDataBinderFactory;
import cc.shacocloud.mirage.utils.LogFormatUtils;
import cc.shacocloud.mirage.utils.MethodParameter;
import cc.shacocloud.mirage.utils.collection.SelfSortList;
import cc.shacocloud.mirage.utils.comparator.AnnotationOrderComparator;
import io.vertx.core.Future;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通过委派给已注册 {@link HandleMethodArgumentResolver}的列表来解析方法参数。
 * 先前解析的方法参数将被缓存，以加快查找速度。
 */
@Slf4j
public class HandleMethodArgumentResolverComposite implements HandleMethodArgumentResolver {
    
    private final List<HandleMethodArgumentResolver> argumentResolvers = new SelfSortList<>(AnnotationOrderComparator.INSTANCE::getOrder);
    
    private final Map<MethodParameter, HandleMethodArgumentResolver> argumentResolverCache =
            new ConcurrentHashMap<>(256);
    
    public HandleMethodArgumentResolverComposite() {
    }
    
    public HandleMethodArgumentResolverComposite(HandleMethodArgumentResolver... resolver) {
        addResolvers(resolver);
    }
    
    /**
     * 添加 {@link HandleMethodArgumentResolver}.
     */
    public HandleMethodArgumentResolverComposite addResolver(HandleMethodArgumentResolver resolver) {
        this.argumentResolvers.add(resolver);
        return this;
    }
    
    /**
     * 添加 {@link HandleMethodArgumentResolver}.
     */
    public HandleMethodArgumentResolverComposite addResolverIfNotContains(HandleMethodArgumentResolver resolver) {
        if (!this.argumentResolvers.contains(resolver)) {
            addResolver(resolver);
        }
        return this;
    }
    
    /**
     * 添加 {@link HandleMethodArgumentResolver HandlerMethodArgumentResolvers}.
     */
    public HandleMethodArgumentResolverComposite addResolvers(
            @Nullable HandleMethodArgumentResolver... resolvers) {
        
        if (resolvers != null) {
            Collections.addAll(this.argumentResolvers, resolvers);
        }
        return this;
    }
    
    /**
     * 添加 {@link HandleMethodArgumentResolver HandlerMethodArgumentResolvers}.
     */
    public HandleMethodArgumentResolverComposite addResolvers(
            @Nullable List<? extends HandleMethodArgumentResolver> resolvers) {
        
        if (resolvers != null && !resolvers.isEmpty()) {
            this.argumentResolvers.addAll(resolvers);
        }
        return this;
    }
    
    /**
     * 返回所有的解析程序的只读列表，或者返回空列表。
     */
    public List<HandleMethodArgumentResolver> getResolvers() {
        return Collections.unmodifiableList(this.argumentResolvers);
    }
    
    /**
     * 清除已配置解析器的列表
     */
    public void clear() {
        this.argumentResolvers.clear();
    }
    
    
    /**
     * 循环已注册的 {@link HandleMethodArgumentResolver}
     * 寻找支持给定的 {@linkplain MethodParameter } 方法参数的处理器
     */
    @Override
    public boolean supportsParameter(@NotNull MethodParameter parameter) {
        return getArgumentResolver(parameter) != null;
    }
    
    /**
     * 循环已注册的 {@link HandleMethodArgumentResolver}
     * 寻找支持给定的 {@linkplain MethodParameter } 方法参数的处理器处理执行
     *
     * @throws IllegalArgumentException 如果找不到合适的参数解析器则抛出例外！
     */
    @Override
    public Future<Object> resolveArgument(@NotNull HttpRequest request,
                                          @NotNull MethodParameter parameter,
                                          @Nullable WebDataBinderFactory binderFactory) {
        if (log.isDebugEnabled()) {
            log.debug("待解析的方法参数 : " + parameter.getParameterType().getName());
        }
        
        HandleMethodArgumentResolver resolver = getArgumentResolver(parameter);
        if (resolver == null) {
            return Future.failedFuture(new IllegalArgumentException("不支持解析参数类型 [" + parameter.getParameterType().getName() + "]。" +
                    " 获取应该调用 supportsParameter 来判断是否支持！"));
        }
        
        if (log.isDebugEnabled()) {
            log.debug("匹配到参数解析处理器：" + resolver);
        }
        
        return resolver.resolveArgument(request, parameter, binderFactory)
                .onSuccess(body -> {
                    if (log.isDebugEnabled()) {
                        log.debug("方法参数 '" + parameter.getParameterType().getName() + "' 解析数据为： [" + LogFormatUtils.formatValue(body, false) + "]");
                    }
                });
    }
    
    /**
     * 查找支持给定方法参数的注册{@link HandleMethodArgumentResolver}
     */
    @Nullable
    private HandleMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
        HandleMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
        if (result == null) {
            for (HandleMethodArgumentResolver resolver : this.argumentResolvers) {
                if (resolver.supportsParameter(parameter)) {
                    result = resolver;
                    this.argumentResolverCache.put(parameter, result);
                    break;
                }
            }
        }
        return result;
    }
    
}
